package com.ccloud.auth.core.handler;

import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import com.ccloud.api.system.model.SysLoginModel;
import com.ccloud.api.system.service.ISysUserService;
import com.ccloud.common.core.constant.CacheConstants;
import com.ccloud.common.core.exception.CustomException;
import com.ccloud.common.core.reqres.response.Result;
import com.ccloud.common.core.utils.JwtUtil;
import com.ccloud.common.core.utils.ValueUtils;
import com.ccloud.common.redis.cache.ZCache;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.CharSet;
import org.apache.commons.lang.CharUtils;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证成功处理程序
 *
 * @author chentl
 * @version V1.0
 * @project bigdata-cloud
 * @title CAuthenticationSuccessHandler
 * @package com.ccloud.auth.core.handler
 * @date 2022/3/23 9:58 上午
 * @copyright: 武汉大数据产业发展有限公司 All rights reserved.
 */
@Slf4j
@Component
public class CAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Resource
    private ZCache zCache;

    @Resource
    private ObjectMapper mapper;

    @Resource
    private ClientDetailsService clientDetailsService;

    @Resource(name = "tokenServices")
    private AuthorizationServerTokenServices tokenServices;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private ISysUserService sysUserService;

    /**
     * 身份验证成功
     *
     * @param request        请求
     * @param response       响应
     * @param authentication 身份验证
     * @throws java.io.IOException IOException
     * @throws ServletException    Servlet异常
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        String header = request.getHeader("Authorization");

        // 1. 从请求头中获取 ClientId
        if (header == null || !header.startsWith("Basic ")) {
            throw new UnapprovedClientAuthenticationException("请求头中无client信息");
        }

        //2. 获取入参
        String captcha = ValueUtils.parseString(request.getParameter("captcha"));
        String checkKey = ValueUtils.parseString(request.getParameter("checkKey"));
        String username = ValueUtils.parseString(request.getParameter("username"));
        String password = ValueUtils.parseString(request.getParameter("password"));
        SysLoginModel sysLoginModel = SysLoginModel.builder().captcha(captcha)
                .checkKey(checkKey)
                .password(password)
                .username(username)
                .build();

        //3. 执行系统业务登录
        Result<Map<String, Object>> result = sysUserService.login(sysLoginModel);

        if (result.getCode() != HttpServletResponse.SC_OK) {
            throw new CustomException(MessageFormat.format("登录失败: {0}", result.getMessage()));
        }

        //4. 封装用户信息 返回 Token
        String[] tokens = this.extractAndDecodeHeader(header);
        String clientId = tokens[0];
        String clientSecret = tokens[1];
        TokenRequest tokenRequest;
        //5. 通过 ClientDetailsService 获取 ClientDetails
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
        //6. 校验 ClientId和 ClientSecret的正确性
        if (clientDetails == null) {
            throw new UnapprovedClientAuthenticationException("clientId:" + clientId + "对应的信息不存在");
        } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
            throw new UnapprovedClientAuthenticationException("clientSecret不正确");
        } else {
            //7. 通过 TokenRequest构造器生成 TokenRequest
            tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "custom");
        }
        //8. 通过 TokenRequest的 createOAuth2Request方法获取 OAuth2Request
        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
        //9. 通过 Authentication和 OAuth2Request构造出 OAuth2Authentication
        OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
        //10. 通过 AuthorizationServerTokenServices 生成 OAuth2AccessToken
        OAuth2AccessToken token = tokenServices.createAccessToken(auth2Authentication);
        //11. 设置token缓存有效时间
        zCache.put(CacheConstants.PREFIX_USER_TOKEN + token.getValue(), token, JwtUtil.EXPIRE_TIME);


        Map<String, Object> resultMap = result.getResult();
        resultMap.put("token", token);
        result.setResult(resultMap);
        log.info("登录成功");


        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(CharsetUtil.UTF_8);
        response.getWriter().write(mapper.writeValueAsString(result));

    }

    /**
     * 抽取请求头中加密的 basic 卡密
     *
     * @param header 请求头
     * @return {@link String[]}
     * @author chentl
     * @version v1.0.0
     * @since 10:57 上午 2022/3/24
     **/
    private String[] extractAndDecodeHeader(String header) {
        byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
        byte[] decoded;
        try {
            decoded = Base64.getDecoder().decode(base64Token);
        } catch (IllegalArgumentException var7) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }
        String token = new String(decoded, StandardCharsets.UTF_8);
        int delim = token.indexOf(":");
        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        } else {
            return new String[]{token.substring(0, delim), token.substring(delim + 1)};
        }
    }

    public static void main(String[] args) {
        System.out.println("admin:" + new BCryptPasswordEncoder().encode("123123"));
        System.out.println("test:" + new BCryptPasswordEncoder().encode("123123"));
        System.out.println("test1:" + new BCryptPasswordEncoder().encode("123123"));
    }

}